import java.text.DecimalFormat;
import java.util.PriorityQueue;

// by Sacha Best and Brian Bagdasarian

public class Stock
{
	public static final DecimalFormat money = new DecimalFormat("0.00");
	
	private String symbol, name;
	private int volume;
	private double price, lowPrice, highPrice, openingPrice;
	private PriorityQueue<TradeOrder> buyOrders, sellOrders;
	
	public Stock(String symbol, String name, double price)
	{
		this.symbol = symbol;
		this.name = name;
		this.price = price;
		this.lowPrice = price;
		this.highPrice = price;
		this.openingPrice = price;
		this.volume = 0;
		buyOrders = new PriorityQueue<TradeOrder>(16, new PriceComparator(false));
		sellOrders = new PriorityQueue<TradeOrder>(16, new PriceComparator(true));
	}
	public Stock(String symbol, double price)
	{
		this(symbol, symbol, price);
	}
	public Stock()
	{
		this(null, null, 0);
	}
	
	public String getQuote()
	{
		String normal = "Price: " + money.format(price) + " ";
		String high = " hi: " + money.format(highPrice) + " ";
		String low = " lo: " + money.format(lowPrice) + " ";
		String volume = " vol: " + this.volume + " ";
		String ask = "Ask: none "; //temporary placeholder
		String size = ""; //temporary placeholder
		if (!sellOrders.isEmpty())
		{
			double price = 0;;
			if (sellOrders.peek().isMarket())
				price = this.price;
			else
				price = sellOrders.peek().getPrice();
			ask = "Ask: " + money.format(price) + " "; 
			size = "size: " + sellOrders.peek().getNumShares() + " ";
		}
		String bid = "  Bid: none"; //temporary placeholder
		String buySize = ""; //temporary placeholder
		if (!buyOrders.isEmpty())
		{
			double price = 0;;
			if (buyOrders.peek().isMarket())
				price = this.price;
			else
				price = buyOrders.peek().getPrice();
			bid = " Bid: " + money.format(price) + " "; 
			buySize = "size: " + buyOrders.peek().getNumShares() + " "; 
		}
		return name + "  " + "(" + symbol +")" + "\n" + normal + high + low + volume + "\n" + ask + size + bid + buySize; 
	}
	public void placeOrder(TradeOrder order)
	{
		String message = "New order:";
		if (order.isBuy())
		{
			buyOrders.add(order);
			message += (" Buy: ");
		}
		else
		{
			sellOrders.add(order);
			message += (" Sell: ");
		}
		message += (order.getSymbol());
		message += (" (" + name + ")\n");
		message += order.getNumShares() + " shares";
		if (order.isLimit())
			message += " at $" + money.format(order.getPrice());
		else 
			message += " at market";
		order.getTrader().recieveMessage(message);
		executeOrders();
	}
	protected void executeOrders()
	{
		while (!buyOrders.isEmpty() && !sellOrders.isEmpty())
		{
			boolean finalCase = buyOrders.peek().isLimit() && sellOrders.peek().isLimit();
			finalCase = finalCase && buyOrders.peek().getPrice() < sellOrders.peek().getPrice();
			if (!finalCase) //written like this for readability
				executeOrders(buyOrders.peek(), sellOrders.peek());
		}
	}
	private void executeOrders(TradeOrder buy, TradeOrder sell)
	{
		if (buy == null || sell == null) 
			return;
		int numTraded = 0;
		double price = 0;
		if (buy.isLimit() && sell.isLimit() && buy.getPrice() <= sell.getPrice())
			price = sell.getPrice();
		else if ((buy.isMarket() && sell.isLimit()))
			price = sell.getPrice();
		else if ((buy.isLimit() && sell.isMarket()))
			price = buy.getPrice();
		else //both market
			price = this.price;
		numTraded = transact(buy, sell, price);
		if (price > highPrice) //reset stock prices
			highPrice = price;
		else if (price < lowPrice) 
			lowPrice = price;
		volume += numTraded;
		double amount = numTraded * price;
		String message = numTraded + " " + symbol + " at $" + money.format(price) + " amt " + money.format(amount);
		buy.getTrader().recieveMessage("You bought: " + message);
		sell.getTrader().recieveMessage("You sold: " + message);
		if (buy.getNumShares() <= 0) 
			buyOrders.remove();
		if (sell.getNumShares() <= 0) 
			sellOrders.remove();
	}
	private int transact(TradeOrder buy, TradeOrder sell, double price)
	{
		int numTraded = Math.min(buy.getNumShares(), sell.getNumShares());
		buy.subtractShares(numTraded);
		sell.subtractShares(numTraded);
		return numTraded;
	}
}
